home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 18 / CU Amiga Magazine's Super CD-ROM 18 (1997)(EMAP Images)(GB)[!][issue 1998-01].iso / CUCD / Online / hsc / source / ugly / umemory.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-11-02  |  15.1 KB  |  752 lines

  1. /*
  2.  * This source code is part of hsc, a html-preprocessor,
  3.  * Copyright (C) 1993-1997  Thomas Aglassinger
  4.  *
  5.  * This program is free software; you can redistribute it and/or modify
  6.  * it under the terms of the GNU General Public License as published by
  7.  * the Free Software Foundation; either version 2 of the License, or
  8.  * (at your option) any later version.
  9.  *
  10.  * This program is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program; if not, write to the Free Software
  17.  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18.  *
  19.  */
  20. /*
  21.  * ugly/umemory.c
  22.  *
  23.  * additional memory manegment functions;
  24.  * implements some parts of Amiga-developer-tool
  25.  * "MungWall" at source-level
  26.  *
  27.  * updated: 12-May-1997
  28.  * created: 29-Mar-1994
  29.  *
  30.  */
  31.  
  32. /*
  33.  *
  34.  * Memory munging:
  35.  *
  36.  *   Except for ucalloc(), memory is pre-munged on allocation with
  37.  *   $DEADFOOD. When this is used in an Enforcer report, the caller is
  38.  *   allocating memory and doesn't initialize it before using it.
  39.  *
  40.  *   Memory is filled with $DEADBEEF before it is freed, encouraging
  41.  *   programs reusing free'ed memory to crash.
  42.  *
  43.  * Memory watching:
  44.  *
  45.  *   Null sized malloc()'s are reported. The integrity of the walls will
  46.  *   be tested according to the size specified when free'ed.
  47.  *
  48.  */
  49.  
  50. #include <stdio.h>
  51. #include <stdlib.h>
  52. #include <string.h>
  53.  
  54. #include "utypes.h"
  55.  
  56. #define NOEXTERN_UGLY_UMEMORY_H
  57. #include "umemory.h"
  58.  
  59. /*
  60.  * size of wall build around every memory block
  61.  */
  62. #define UMEM_WALLSIZE 16
  63.  
  64. /*
  65.  * blocksize memory allocations are rounded up by OS
  66.  * (this one's only needed to compute the amount of
  67.  * slack-memory and won't cause any problems if wrong)
  68.  */
  69. #if defined(AMIGA)
  70. #define UMEM_BLOCKSIZE 8    /* AmigaOS */
  71. #else
  72. #define UMEM_BLOCKSIZE 8
  73. #endif
  74.  
  75. #ifndef modfit
  76. #define modfit(x,by) ((by)*(((x)+(by-1))/(by)))
  77. #endif
  78.  
  79. static UGLYMEM *first = NULL;
  80.  
  81. static UBYTE deadbeef[4] =
  82. {0xDE, 0xAD, 0xBE, 0xEF};    /* used to fill mem after free() */
  83. static UBYTE deadfood[4] =
  84. {0xDE, 0xAD, 0xF0, 0x0D};    /* used to fill mem after malloc() */
  85.  
  86. static UBYTE ugly_fillchar = 0x81;
  87.  
  88. static ULONG ugly_umalloc_count = 0;    /* num. of calls to umalloc()/ucalloc() */
  89. static ULONG ugly_ufree_count = 0;    /* num. of calls to ufree() */
  90. static ULONG ugly_umalloc_count_fail = 0;    /* num. of failed calls to umalloc() */
  91. static ULONG ugly_ufree_count_fail = 0;        /* num. of failed calls to ufree() */
  92. static ULONG ugly_maxmem_usage = 0;    /* maximum memmory used */
  93. static ULONG ugly_curmem_usage = 0;    /* current memory used */
  94. static ULONG ugly_real_maxmem_usage = 0;    /* maximum memmory used */
  95. static ULONG ugly_real_curmem_usage = 0;    /* current memory used */
  96. static ULONG ugly_maxnod_usage = 0;    /* maximum num. of memmory nodes used */
  97. static ULONG ugly_curnod_usage = 0;    /* current num. of memory nodes used */
  98.  
  99. /* forward reference */
  100. void *ugly_malloc_notracking(size_t size);
  101. static BOOL ugly_walldamaged(UGLYMEM * umem);
  102.  
  103. /* function pointer for nomem-handler */
  104. BOOL(*ugly_nomem_handler) (size_t size) = NULL;
  105.  
  106. /*
  107.  * send panic message to stderr
  108.  */
  109. VOID display_panic_message(char *msg, char *file, size_t line)
  110. {
  111.     fprintf(stderr, "\n\n");
  112.     fprintf(stderr, "  I won't be a monkey in anyone's zoo\n");
  113.     fprintf(stderr, "  I won't get fazed whatever you do\n");
  114.     fprintf(stderr, "                   (Ride, \"Not Fazed\")\n\n");
  115.     fprintf(stderr, "** internal error: \"%s\" (%lu): %s\n", \
  116.         file, (unsigned long) line, msg);
  117.     exit(255);
  118. }
  119.  
  120. /*
  121.  * find_umem
  122.  */
  123. static UGLYMEM *find_umem(void *mem)
  124. {
  125.     UGLYMEM *nxtum = first;
  126.     UGLYMEM *found = NULL;
  127.  
  128.     while (nxtum && (!found))
  129.     {
  130.     if (nxtum->ptr == mem)
  131.     {
  132.         found = nxtum;
  133.     }
  134.     nxtum = nxtum->next;
  135.     }
  136.  
  137. #if DEBUG_UGLY_MEMORY==2
  138.     if (!found)
  139.     {
  140.     fprintf(stderr, "*memory* FIND_UMEM: couln't find %p\n", mem);
  141.     }
  142. #endif
  143.  
  144.     return (found);
  145. }
  146.  
  147. /*
  148.  * find_prev
  149.  */
  150. static UGLYMEM *find_prev(UGLYMEM * umem)
  151. {
  152.     UGLYMEM *prev = first;
  153.     UGLYMEM *pprev = NULL;
  154.     BOOL found = FALSE;
  155.  
  156.     while (prev && (!found))
  157.     {
  158.     found = (prev == umem);
  159.     if (!found)
  160.     {
  161.         pprev = prev;
  162.         prev = prev->next;
  163.     }
  164.     }
  165.  
  166.     return (pprev);
  167. }
  168.  
  169. /*
  170.  * fill_mem4, fill_mem
  171.  *
  172.  * fill memory with value specified
  173.  */
  174. static void fill_mem4(void *mem, size_t size, UBYTE value[4])
  175. {
  176.     size_t i;
  177.  
  178.     for (i = 0; i < size; i++)
  179.     {
  180.     (((UBYTE *) mem)[i]) = value[i % 4];
  181.     }
  182. }
  183.  
  184. static void fill_mem(void *mem, size_t size, UBYTE value)
  185. {
  186.     size_t i;
  187.  
  188.     for (i = 0; i < size; i++)
  189.     {
  190.     (((UBYTE *) mem)[i]) = value;
  191.     }
  192. }
  193.  
  194. /*
  195.  * del_uglymem
  196.  *
  197.  * free an uglymem-entry
  198.  */
  199. static void del_uglymem(UGLYMEM * umem)
  200. {
  201.     UGLYMEM *prev = find_prev(umem);
  202.  
  203.     /* unlink from list */
  204.     if (prev)
  205.     {
  206.     prev->next = umem->next;
  207.     }
  208.     else
  209.     {
  210.     first = umem->next;
  211.     }
  212.  
  213.     /* check for damaged wall */
  214.     if (!ugly_walldamaged(umem))
  215.     {
  216.     /* wall ok:
  217.      *
  218.      * fill memory with $DEADBEEF,
  219.      * free memory */
  220.     fill_mem4(umem->lower, umem->size + 2 * UMEM_WALLSIZE, deadbeef);
  221.     free(umem->lower);
  222.     }
  223.  
  224.     /* free memory structure */
  225.     umem->lower = NULL;
  226.     umem->upper = NULL;
  227.     umem->size = 0;
  228.     umem->file = NULL;
  229.     umem->line = 0;
  230.     free(umem);
  231. }
  232.  
  233. /*
  234.  * new uglymem
  235.  *
  236.  * alloc & init a new entry of ugly mem
  237.  */
  238. static UGLYMEM *new_uglymem(size_t memsize, STRPTR memfile, ULONG memline)
  239. {
  240.     UGLYMEM *newmem = (UGLYMEM *) malloc(sizeof(UGLYMEM));
  241.  
  242.     if (newmem)
  243.     {
  244.     newmem->lower = (UBYTE *) ugly_malloc_notracking(memsize
  245.                                + 2 * UMEM_WALLSIZE);
  246.     if (newmem->lower)
  247.     {
  248.         /* compute location of main mem/upper wall */
  249.         newmem->ptr = (void *) (newmem->lower + UMEM_WALLSIZE);
  250.         newmem->upper = (newmem->lower + UMEM_WALLSIZE + memsize);
  251.  
  252.         /* link to list */
  253.         newmem->next = first;
  254.         first = newmem;
  255.  
  256.         /* init data */
  257.         newmem->size = memsize;
  258.         newmem->file = memfile;
  259.         newmem->line = memline;
  260.         newmem->fillchar = ugly_fillchar;
  261.  
  262.         /* fill new mem area with $DEADF00D */
  263.         fill_mem4(newmem->ptr, memsize, deadfood);
  264.  
  265.         /* fill lower/upper wall */
  266.         fill_mem(newmem->lower, UMEM_WALLSIZE, ugly_fillchar);
  267.         fill_mem(newmem->upper, UMEM_WALLSIZE, ugly_fillchar);
  268.  
  269.         /* update fillchar */
  270.         if (ugly_fillchar == 0xff)
  271.         {
  272.         ugly_fillchar = 0x81;
  273.         }
  274.         else
  275.         {
  276.         ugly_fillchar++;
  277.         }
  278.     }
  279.     else
  280.         free(newmem);
  281.     }
  282.     return (newmem);
  283. }
  284.  
  285. static void uglymem_message(STRPTR msg)
  286. {
  287.     fprintf(stderr, "%s\n", msg);
  288. }
  289.  
  290. static void ugly_memdump(void *ptr, size_t size)
  291. {
  292.     STRPTR data = (STRPTR) ptr;
  293.  
  294.     /* limit size */
  295.     if (size > 16)
  296.     {
  297.     size = 16;
  298.     }
  299.  
  300.     fprintf(stderr, "  %p:", ptr);
  301.     if (data)
  302.     {
  303.     size_t i;
  304.  
  305.     /* hex dump */
  306.     for (i = 0; i < size; i++)
  307.     {
  308.         if (!(i % 4))
  309.         {
  310.         fprintf(stderr, " ");
  311.         }
  312.         fprintf(stderr, "%02x", data[i]);
  313.  
  314.     }
  315.  
  316.     /* fill with blanks */
  317.     while (i < 16)
  318.     {
  319.         if (!(i % 4))
  320.         {
  321.         fprintf(stderr, " ");
  322.         }
  323.         fprintf(stderr, "  ");
  324.         i++;
  325.     }
  326.  
  327.     fprintf(stderr, "  \"");
  328.     /* ascii dump */
  329.     for (i = 0; i < size; i++)
  330.     {
  331.         if (data[i] < ' ')
  332.         {
  333.         fprintf(stderr, ".");
  334.         }
  335.         else
  336.         {
  337.         fprintf(stderr, "%c", data[i]);
  338.         }
  339.     }
  340.     fprintf(stderr, "\"\n");
  341.  
  342.     }
  343.     else
  344.     fprintf(stderr, "NULL\n");
  345.  
  346. }
  347.  
  348. static void uglymem_meminfo(void *ptr, STRPTR file, ULONG line)
  349. {
  350.     fprintf(stderr, "  %p: from \"%s\" (%lu)\n", ptr, file, line);
  351. }
  352.  
  353. static void umem_info(UGLYMEM * umem)
  354. {
  355.     fprintf(stderr, "  %p: %lu (0x%lx) bytes from \"%s\" (%lu)\n",
  356.         umem->ptr, (ULONG) umem->size, (ULONG) umem->size,
  357.         umem->file, umem->line);
  358. }
  359.  
  360. /*
  361.  *-------------------------------------
  362.  * wall check functions
  363.  *-------------------------------------
  364.  */
  365.  
  366. /*
  367.  * str_ubyte
  368.  *
  369.  * convert a UBYTE-value to hex/dez/char and return
  370.  * results as a displayable string
  371.  */
  372. static STRPTR str_ubyte(UBYTE val)
  373. {
  374.     static STRARR strbuf[30];
  375.     UBYTE ch = val;
  376.  
  377.     if (ch < 32)
  378.     ch = '.';
  379.  
  380.     sprintf(strbuf, "(0x%02x/#%d/`%c')", val, val, ch);
  381.  
  382.     return (strbuf);
  383. }
  384.  
  385. /*
  386.  * ugly_walldamaged
  387.  *
  388.  * check memory walls a specifc entry in memory-list,
  389.  * output message if wall is damaged
  390.  */
  391. static BOOL ugly_walldamaged(UGLYMEM * umem)
  392. {
  393.     size_t i = 0;
  394.     BOOL damaged = FALSE;
  395.  
  396.     while (!damaged && (i < UMEM_WALLSIZE))
  397.     {
  398.     BOOL lower_damaged = (umem->lower[i] != umem->fillchar);
  399.     BOOL upper_damaged = (umem->upper[i] != umem->fillchar);
  400.  
  401.     damaged = lower_damaged || upper_damaged;
  402.     if (damaged)
  403.     {
  404.         STRPTR wall;
  405.         UBYTE value;
  406.  
  407.         if (lower_damaged)
  408.         {
  409.         wall = "LOWER";
  410.         value = umem->lower[i];
  411.         }
  412.         else
  413.         {
  414.         wall = "UPPER";
  415.         value = umem->upper[i];
  416.         }
  417.  
  418.         fprintf(stderr, "*** MEMORY WALL DAMAGED!!!\n");
  419.         fprintf(stderr, "*** %s wall, byte#%lu is %s instead of 0x%02x\n",
  420.             wall, (ULONG) i, str_ubyte(value), umem->fillchar);
  421.         umem_info(umem);
  422.         ugly_memdump(umem->ptr, umem->size);
  423.         fprintf(stderr, "  * lower wall:\n");
  424.         ugly_memdump(umem->lower, UMEM_WALLSIZE);
  425.         fprintf(stderr, "  * upper wall:\n");
  426.         ugly_memdump(umem->upper, UMEM_WALLSIZE);
  427.  
  428.     }
  429.     else
  430.     {
  431.         i++;
  432.     }
  433.     }
  434.  
  435.     return (damaged);
  436. }
  437.  
  438. /*
  439.  * uglymem_wallcheck
  440.  *
  441.  * display a header message and check all walls for consistency
  442.  */
  443. void uglymem_wallcheck(STRPTR msg, STRPTR file, ULONG line)
  444. {
  445.     UGLYMEM *umem = first;
  446.  
  447.     if (umem)
  448.     {
  449.     /* report header */
  450.     fprintf(stderr, "MEMORY WALL-CHECK (%s)", msg);
  451.     if (file)
  452.         fprintf(stderr, " from `%s' (%lu)", file, line);
  453.     fprintf(stderr, "\n");
  454.  
  455.     /* check all elements */
  456.     while (umem)
  457.     {
  458.         if (umem->ptr)
  459.         {
  460.         ugly_walldamaged(umem);
  461.         umem = umem->next;
  462.         }
  463.         else
  464.         {
  465.         umem = NULL;
  466.         fprintf(stderr, "\n** PANIC: memory list trashed\n");
  467.         }
  468.     }
  469.     }
  470. }
  471.  
  472. /*
  473.  *-------------------------------------
  474.  * memory statistics functions
  475.  *-------------------------------------
  476.  */
  477.  
  478. /*
  479.  * ugly_mem_report
  480.  *
  481.  * displaly all memory nodes currently allocated
  482.  */
  483. void uglymem_report(STRPTR msg, STRPTR file, ULONG line, STRPTR date, STRPTR time)
  484. {
  485.     UGLYMEM *umem = first;
  486.  
  487.     if (umem)
  488.     {
  489.     /* report header */
  490.     fprintf(stderr, "MEMORY REPORT (%s)\n", msg);
  491.     if (file)
  492.     {
  493.         fprintf(stderr, "(\"%s\" (%lu), at %s, %s)\n",
  494.             file, line, date, time);
  495.     }
  496.  
  497.     /* print all elements */
  498.     while (umem)
  499.     {
  500.         if (umem->ptr)
  501.         {
  502.         umem_info(umem);
  503.         ugly_memdump(umem->ptr, umem->size);
  504.         umem = umem->next;
  505.         }
  506.         else
  507.         {
  508.         umem = NULL;
  509.         fprintf(stderr, "##\n## panic: memory list trashed\n##\n");
  510.         }
  511.     }
  512.     }
  513. }
  514.  
  515. /*
  516.  * ugly_mem_stats
  517.  *
  518.  * display memory statistics (nodes & size allocated)
  519.  */
  520. void uglymem_stats(STRPTR msg, STRPTR file, ULONG line, STRPTR date, STRPTR time)
  521. {
  522.     /* statistics header */
  523.     fprintf(stderr, "MEMORY STATISTICS (%s)\n", msg);
  524.     if (file)
  525.     {
  526.     fprintf(stderr, "(\"%s\" (%lu), at %s, %s)\n",
  527.         file, line, date, time);
  528.     }
  529.  
  530.     /* memory statistics */
  531.     fprintf(stderr, "  bytes used: %lu max: %lu/%lu  ",
  532.         ugly_curmem_usage, ugly_real_maxmem_usage,
  533.         ugly_maxmem_usage);
  534.     if (ugly_maxmem_usage)
  535.     {
  536.     fprintf(stderr, "slack: %lu%%\n",
  537.         (100 * (ugly_real_maxmem_usage - ugly_maxmem_usage))
  538.         / ugly_maxmem_usage);
  539.     }
  540.     else
  541.     {
  542.     fprintf(stderr, "no slack\n");
  543.     }
  544.  
  545.     fprintf(stderr, "  nodes used: %lu (max: %lu)\n",
  546.         ugly_curnod_usage, ugly_maxnod_usage);
  547.     fprintf(stderr, "  calls to: umalloc(%lu)   ufree(%lu)\n",
  548.         ugly_umalloc_count, ugly_ufree_count);
  549. }
  550.  
  551. /*
  552.  *-------------------------------------
  553.  * atexit functions
  554.  *-------------------------------------
  555.  */
  556.  
  557. /*
  558.  * atexit_uglymemory_real
  559.  */
  560. void atexit_uglymemory_real(void)
  561. {
  562.     ULONG mem_lost = ugly_curmem_usage;
  563.     uglymem_report("at exit:  MEMORY LEAK detected!",
  564.            NULL, 0, NULL, NULL);
  565.     uglymem_stats("[exit]", NULL, 0, NULL, NULL);
  566.  
  567.     /* release all lost mem */
  568.     while (first)
  569.     {
  570.     del_uglymem(first);
  571.     }
  572.  
  573.     if (mem_lost)
  574.     {
  575.     fprintf(stderr, "\n%lu bytes of memory lost!\n", mem_lost);
  576.     }
  577. }
  578.  
  579. /*
  580.  * atexit_uglymemory_dummy
  581.  */
  582. void atexit_uglymemory_dummy(void)
  583. {
  584.     /* do nufin */
  585. }
  586.  
  587. /*
  588.  *-------------------------------------
  589.  * memory handling functions
  590.  *-------------------------------------
  591.  */
  592.  
  593. /*
  594.  * ugly_malloc_notracking
  595.  */
  596. void *ugly_malloc_notracking(size_t size)
  597. {
  598.     void *mem;
  599.     BOOL retry;
  600.  
  601.     do
  602.     {
  603.     mem = malloc(size);
  604.     if (!mem && ugly_nomem_handler)
  605.     {
  606.         /* call nomem-handler */
  607.         retry = (*ugly_nomem_handler) (size);
  608.         if (!retry)
  609.         {
  610.         exit(EXIT_FAILURE);    /* abort programm */
  611.         }
  612.     }
  613.     else
  614.     {
  615.         retry = FALSE;
  616.     }
  617.     }
  618.     while (retry);
  619.  
  620.     return (mem);
  621. }
  622.  
  623. /*
  624.  * ugly_malloc_tracking
  625.  */
  626. void *ugly_malloc_tracking(size_t size, STRPTR file, ULONG line)
  627. {
  628.     void *mem = NULL;
  629.     UGLYMEM *umem = NULL;
  630.  
  631. #if DEBUG_UGLY_MEMORY==2
  632.     fprintf(stderr, "*memory* UMALLOC() from `%s' (%lu)\n", file, line);
  633. #endif
  634.     if (size)
  635.     {
  636.     /* update num. of calls to umalloc() */
  637.     ugly_umalloc_count++;
  638.  
  639.     /* alloc new uglymem */
  640.     umem = new_uglymem(size, file, line);
  641.     if (umem)
  642.     {
  643.         mem = umem->ptr;
  644.  
  645.         /* update memory usage and num of nodes */
  646.         ugly_curmem_usage += size;
  647.         ugly_real_curmem_usage += modfit(size, UMEM_BLOCKSIZE);
  648.         if (ugly_curmem_usage > ugly_maxmem_usage)
  649.         ugly_maxmem_usage = ugly_curmem_usage;
  650.         if (ugly_real_curmem_usage > ugly_real_maxmem_usage)
  651.         ugly_real_maxmem_usage = ugly_real_curmem_usage;
  652.         ugly_curnod_usage++;
  653.         if (ugly_curnod_usage > ugly_maxnod_usage)
  654.         ugly_maxnod_usage = ugly_curnod_usage;
  655.     }
  656.     }
  657.     else
  658.     {
  659.     /* zero-alloc */
  660.  
  661.     /* update num. of failed calls to umalloc() */
  662.     ugly_umalloc_count_fail++;
  663.  
  664.     uglymem_message("MALLOC: zero-sized allocation");
  665.     uglymem_meminfo(NULL, file, line);
  666.     }
  667.  
  668.     return (mem);
  669. }
  670.  
  671. /*
  672.  * ugly_free
  673.  */
  674. void ugly_free(void *ptr, STRPTR file, ULONG line)
  675. {
  676. #if DEBUG_UGLY_MEMORY==2
  677.     fprintf(stderr, "*memory* UFREE() from `%s' (%lu)\n", file, line);
  678. #elif 0
  679.     fputc('.', stderr);        /* still alive? */
  680.     fflush(stderr);
  681. #endif
  682.     if (ptr)
  683.     {
  684.  
  685.     UGLYMEM *umem = find_umem(ptr);
  686.  
  687.     if (umem)
  688.     {
  689.         /* update num. of calls to ufree() */
  690.         ugly_ufree_count++;
  691.  
  692.         /* update memory usage */
  693.         ugly_curmem_usage -= umem->size;
  694.         ugly_real_curmem_usage -= modfit(umem->size, UMEM_BLOCKSIZE);
  695.  
  696.         /* remove node from mem-list */
  697.         del_uglymem(umem);
  698.         ugly_curnod_usage--;
  699.     }
  700.     else
  701.     {
  702.         /* ptr has never been allocated */
  703.  
  704.         /* update num. of calls to ufree() */
  705.         ugly_ufree_count_fail++;
  706.  
  707.         /* -> error message */
  708.         uglymem_message("*** FREE: memory never allocated "
  709.                 " or released twice");
  710.         uglymem_meminfo(ptr, file, line);
  711.     }
  712.     }
  713. }
  714.  
  715. /*
  716.  * ugly_realloc
  717.  *
  718.  * replacement of realloc()
  719.  */
  720. void *ugly_realloc(void *ptr, size_t size, STRPTR file, ULONG line)
  721. {
  722.     void *newptr = ugly_malloc_tracking(size, file, line);
  723.     UGLYMEM *umem = find_umem(ptr);
  724.  
  725.     if (newptr && umem)
  726.     {
  727.     /* copy old data */
  728.     memcpy(newptr, umem->ptr, umem->size);
  729.     /* free old memory */
  730.     ugly_free(ptr, file, line);
  731.     }
  732.     return (newptr);
  733. }
  734.  
  735. /*
  736.  * ugly_calloc
  737.  *
  738.  * replacement of calloc()
  739.  */
  740. void *ugly_calloc(size_t count, size_t size, STRPTR file, ULONG line)
  741. {
  742.     /* alloc new mem */
  743.     void *mem = ugly_malloc_tracking(count * size, file, line);
  744.  
  745.     /* fill mem with zero */
  746.     if (mem)
  747.     {
  748.     memset(mem, 0, size * count);
  749.     }
  750.     return (mem);
  751. }
  752.